package ru.ifmo.rybakov.muclumon.msvs.axis2;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.ifmo.rybakov.muclumon.Cluster;
import ru.ifmo.rybakov.muclumon.ClusterState;
import ru.ifmo.rybakov.muclumon.Core;
import ru.ifmo.rybakov.muclumon.InvalidConfigurationException;
import ru.ifmo.rybakov.muclumon.Node;
import ru.ifmo.rybakov.muclumon.NodeState;
import ru.ifmo.rybakov.muclumon.NumericValue;
import ru.ifmo.rybakov.muclumon.Properties;
import ru.ifmo.rybakov.muclumon.PropertyManager;
import ru.ifmo.rybakov.muclumon.Revision;
import ru.ifmo.rybakov.muclumon.StringValue;
import ru.ifmo.rybakov.muclumon.TaskState;

public class Axis2ConversionUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(Axis2ConversionUtils.class);
	
	public static Cluster convert(Revision revision, ClusterInfo clusterInfo) {
		Cluster cluster = new Cluster();
		cluster.setName(clusterInfo.getName());
		cluster.setRevision(revision);
		cluster.addPropertyValue(new StringValue(PropertyManager.get(Properties.CLUSTER_TOPOLOGY), clusterInfo.getTopology().getValue()));
		
		List<Node> nodes = new ArrayList<Node>();
		cluster.setNodes(nodes);
		if (clusterInfo.getNode() != null && clusterInfo.getNode().getNodeInfo() != null) {
			for (NodeInfo nodeInfo : clusterInfo.getNode().getNodeInfo()) {
				Node node = convert(revision, nodeInfo);
				node.setCluster(cluster);
				nodes.add(node);
			}
		}
		return cluster;
	}
	
	private static Node convert(Revision revision, NodeInfo nodeInfo) {
		Node node = new Node();
		node.setName(nodeInfo.getDNSName());
		node.setRevision(revision);
		node.addPropertyValue(new NumericValue(PropertyManager.get(Properties.NODE_CORE_COUNT), nodeInfo.getNumberOfCores()));
		List<Core> cores = new ArrayList<Core>();
		node.setCores(cores);
		if (nodeInfo.getNumberOfCores() != 0) {
			for (int i = 0; i != nodeInfo.getNumberOfCores(); i++) {
				Core core = new Core();
				core.setRevision(revision);
				cores.add(core);
				core.setNode(node);
			}
			if (nodeInfo.getFrequencyOfCores() == null) {
				logger.info("Frequency of cores is unknown for node {}", node.getName());
			} else {
				ArrayOfDouble frequencyArray = nodeInfo.getFrequencyOfCores();
				double[] frequency = frequencyArray.get_double();
				if (frequency == null || frequency.length != nodeInfo.getNumberOfCores()) {
					logger.warn("Invalid frequency data recieved for node {}", node.getName());
				} else {
					for (int i = 0; i != frequency.length; i++) {
						cores.get(i).addPropertyValue(new NumericValue(PropertyManager.get(Properties.CORE_FREQUENCY), frequency[i]));
					}
				}
			}
			if (nodeInfo.getPerformanceOfCores() == null) {
				logger.info("Performance of cores is unknown for node {}", node.getName());
			} else {
				ArrayOfDouble performanceArray = nodeInfo.getPerformanceOfCores();
				double[] performance = performanceArray.get_double();
				if (performance == null || performance.length != nodeInfo.getNumberOfCores()) {
					logger.warn("Invalid performance data recieved for node {}", node.getName());
				} else {
					for (int i = 0; i != performance.length; i++) {
						cores.get(i).addPropertyValue(new NumericValue(PropertyManager.get(Properties.CORE_PERFORMANCE), performance[i]));
					}
				}
			}
			if (nodeInfo.getSoftList() != null) {
				String[] software = nodeInfo.getSoftList().getString();
				if (software != null) {
					for (int i = 0; i != software.length; i++) {
						node.addPropertyValue(new StringValue(PropertyManager.get(Properties.NODE_SOFTWARE), software[i]));
						// System.out.println(software[i]);
					}
				}
			}
		}
		return node;
	}
	
	/**
	 * Converts {@link ClusterStateInfo} Axis2 element to {@link ClusterState} entity.
	 * @param revision Revision to be associated with cluster state entity
	 * @param clusterStateInfo Element to be converted
	 * @param clusterName Name of cluster this state information was retrieved for
	 * @return {@link ClusterState} instance
	 * @throws InvalidConfigurationException If cluster or node not found in configuration
	 */
	public static ClusterState convert(Revision revision, ClusterStateInfo clusterStateInfo, Cluster cluster) throws InvalidConfigurationException {
		ClusterState clusterState = new ClusterState();
		clusterState.setCluster(cluster);
		clusterState.setRevision(revision);
		
		List<NodeState> nodeStates = new ArrayList<NodeState>();
		if (clusterStateInfo.getNode() != null && clusterStateInfo.getNode().getNodeStateInfo() != null) {
			for (NodeStateInfo nodeStateInfo : clusterStateInfo.getNode().getNodeStateInfo()) {
				NodeState nodeState = convert(nodeStateInfo, revision);
				nodeState.setClusterState(clusterState);
				Node node = cluster.getNodeByName(nodeStateInfo.getDNSName());
				if (node == null) {
					logger.debug("Unable to find node for DNS name {}, terminating", nodeStateInfo.getDNSName());
					throw new InvalidConfigurationException("Unknown node DNS name");
				}
				nodeState.setNode(node);
				nodeStates.add(nodeState);
			}
		}
		clusterState.setNodeStates(nodeStates);
		
		return clusterState;
	}
	
	private static NodeState convert(NodeStateInfo nodeStateInfo, Revision revision) {
		NodeState nodeState = new NodeState();
		nodeState.setRevision(revision);
		nodeState.addPropertyValue(new NumericValue(PropertyManager.get(Properties.NODE_STATE_NETWORK_LOAD), nodeStateInfo.getNetLoad()));
		nodeState.addPropertyValue(new NumericValue(PropertyManager.get(Properties.NODE_STATE_FREE_HDD_SIZE), nodeStateInfo.getFreeHDDSize()));
		nodeState.addPropertyValue(new NumericValue(PropertyManager.get(Properties.NODE_STATE_FREE_HDD_PERCENTS), nodeStateInfo.getFreeHDDPercent()));
		nodeState.addPropertyValue(new NumericValue(PropertyManager.get(Properties.NODE_STATE_PROCESSOR_LOAD), nodeStateInfo.getProcLoad()));
		return nodeState;
	}

	@Deprecated
	public static TaskState convert(Revision revision, TaskInfo info) {
		TaskState taskState = new TaskState();
		taskState.setRevision(revision);
//		taskState.addPropertyValue(new StringValue(Properties.TASK_STATE, info.getState().getValue()));
//		taskState.addPropertyValue(new NumericValue(Properties.TASK_STATE_USED_TIME, info.getUsedTime()));
//		taskState.addPropertyValue(new NumericValue(Properties.TASK_STATE_USED_RAM, info.getUsedRAMSize()));
//		taskState.addPropertyValue(new NumericValue(Properties.TASK_STATE_PROCESSOR_LOAD, info.getProcLoad()));
		return taskState;
	}
}
